class h_grab {

	async getChoosedGrabCourseByUserId(userId) {
		let choosedData = await this.redis.grab.get(`ctask@${userId}`, true)
		if(!choosedData) {
			choosedData = await this.mysql.cxxy.select({
				table: [
					'grab_appt a',
					'grab_course c'
				],
				field: [
					'apptId',
					'apptUser',
					'c.courseType',
					'c.courseId',
					'apptStatus',
					'apptDate'
				],
				where: 'a.courseId=c.courseId && a.courseType=0 && c.courseType=0 && a.apptDate >= c.startTime && a.apptDate <= c.endTime && apptUser=$apptUser',
				data: {
					apptUser: userId
				}
			})
			choosedData = {
				userId: userId,
				appts: choosedData
			}
			await this.redis.grab.set(`ctask@${userId}`, choosedData)
			console.log('refresh redis grab course choose list')
		}
		return choosedData.appts
	}

	async getSportsGrabCourseByMajorId(majorId) {
		if(!this._checkMajorId(majorId))
			return this.reject('-3026', 'major id is invalid')
		let sportsGrabCourseData = await this.redis.grab.get(`sportsCourses@${majorId}`, true)
		if(!sportsGrabCourseData) {
			sportsGrabCourseData = await this.mysql.cxxy.select({
				table: [
					'grab_course g',
					'classroom r'
				],
				field: [
					'courseId',
					'courseType',
					'courseTid',
					'courseName',
					'courseTch',
					'courseDay',
					'coursePitch',
					'courseData',
					'majorId',
					'g.roomId',
					'startWeek',
					'endWeek',
					'doubleWeek',
					'startTime',
					'endTime',
					'courseCount',
					'roomName'
				],
				where: 'courseType=1 && endTime>=$nowTime && majorId=$majorId && g.roomId=r.roomId',
				data: {
					nowTime: parseInt(Date.now() / 1000),
					majorId
				}
			})
			await this.redis.grab.set(`sportsCourses@${majorId}`, sportsGrabCourseData)
			console.log('refresh redis grab sports course list')
		}
		return sportsGrabCourseData
	}

	async getChoosedSportsGrabCourseByUserId(userId) {
		let choosedData = await this.redis.grab.get(`stask@${userId}`)
		if(!choosedData) {
			choosedData = await this.mysql.cxxy.select({
				table: [
					'grab_appt a',
					'grab_course c'
				],
				field: [
					'apptId',
					'apptUser',
					'c.courseType',
					'c.courseId',
					'apptStatus',
					'apptDate'
				],
				where: 'a.courseId=c.courseId && a.courseType=1 && c.courseType=1 && a.apptDate >= c.startTime && a.apptDate <= c.endTime && apptUser=$apptUser',
				data: {
					apptUser: userId
				}
			})
			await this.redis.grab.set(`stask@${userId}`, choosedData)
			console.log('refresh redis grab sports course choose list')
		}
		return choosedData
	}

	async addGrabCourseAppt(courseIds, userType, userId, majorId) {
		if(!courseIds || !courseIds instanceof Array || courseIds.length == 0)
			return this.reject('grab course list invalid')
		let apptIds = []
		for(let courseId of courseIds) {
			apptIds.push(await this.crypto.md5('0' + courseId + userType + userId))
		}
		let grabCourses
		if(majorId)
			grabCourses = await this.redis.grab.get(`courses@${majorId}`, true)
		if(grabCourses) {
			grabCourses = grabCourses.filter((course) => {
				return course.courseType == 0 && courseIds.indexOf(course.courseId) != -1
			})
		}
		else {
			grabCourses = await this.mysql.cxxy.select({
				table: 'grab_course',
				field: 'courseId',
				where: `courseType=0 && courseId in ('${courseIds.join(`','`)}')`
			})
			if(!grabCourses[0])
				return this.reject('-2028', 'grab course not opened')
		}
		if(grabCourses.length != courseIds.length)
			return this.reject('-2029', 'partial course has been grab empty')
		let ctask = await this.redis.grab.get(`ctask@${userId}`, true)
		if(!ctask || !ctask.appts || ctask.appts.length == 0) {
			if(!ctask)
				ctask = {}
			ctask.appts = await this.mysql.cxxy.select({
				table: 'grab_appt',
				field: [
					'apptId',
					'apptUser',
					'courseType',
					'courseId',
					'apptStatus',
					'apptDate'
				],
				where: 'courseType=0 && apptUser=$apptUser',
				data: {
					apptUser: userId
				}
			})
		}
		let redisData = []
		let insertData = []
		const nowTime = parseInt(Date.now() / 1000)
		for(let index in apptIds) {
			let exists = false
			for(let appt of ctask.appts) {
				if(appt.apptId == apptIds[index]) {
					exists = true
					break
				}
			}
			if(!exists) {
				if(!courseIds[index])
					return this.reject('-2029', 'partial course has been grab empty')
				insertData.push({
					apptId: apptIds[index],
					apptUserType: userType,
					apptUser: userId,
					courseType: 0,
					courseId: courseIds[index],
					apptDate: nowTime
				}) 
				redisData.push({
					apptId: apptIds[index],
					apptUser: userId,
					courseType: 0,
					courseId: courseIds[index],
					apptStatus: 0,
					apptDate: nowTime
				})
			}
		}
		if(insertData.length != 0) {
			await this.redis.grab.set(`ctask@${userId}`, {
				userId: ctask.userId || userId,
				appts: ctask.appts.concat(redisData)
			})
			this.mysql.cxxy.insert({
				table: 'grab_appt',
				field: [
					'apptId',
					'apptUserType',
					'apptUser',
					'courseType',
					'courseId',
					'apptDate'
				],
				data: insertData
			})
		}
		return courseIds
	}

	async addGrabSportsCourseAppt(courseIds, userType, userId, majorId) {
		if(!courseIds || !courseIds instanceof Array || courseIds.length == 0)
			return this.reject('grab sports course list invalid')
		let apptIds = []
		for(let courseId of courseIds) {
			apptIds.push(await this.crypto.md5('1' + courseId + userType + userId))
		}
		let grabCourses
		if(majorId)
			grabCourses = await this.redis.grab.get(`sportsCourses@${majorId}`, true)
		if(grabCourses) {
			grabCourses = grabCourses.filter((course) => {
				return course.courseType == 0 && courseIds.indexOf(course.courseId) != -1
			})
		}
		else {
			grabCourses = await this.mysql.cxxy.select({
				table: 'grab_course',
				field: 'courseId',
				where: `courseType=0 && courseId in ('${courseIds.join(`','`)}')`
			})
			if(!grabCourses[0])
				return this.reject('-2028', 'grab course not opened')
		}
		if(grabCourses.length != courseIds.length)
			return this.reject('-2029', 'partial course has been grab empty')
		let stask = await this.redis.grab.get(`stask@${userId}`, true)
		if(!stask || stask.length == 0)
			stask = await this.mysql.cxxy.select({
				table: 'grab_appt',
				field: 'apptId',
				where: 'courseType=1 && apptUser=$apptUser',
				data: {
					apptUser: userId
				}
			})
		let insertData = []
		let redisData = []
		let nowTime = parseInt(Date.now() / 1000)
		for(let index in apptIds) {
			let exists = false
			for(let appt of stask) {
				if(appt.apptId == apptIds[index]) {
					exists = true
					break
				}
			}
			if(!exists) {
				if(!courseIds[index])
					return this.reject('-2029', 'partial sports course has been grab empty')
				insertData.push({
					apptId: apptIds[index],
					apptUserType: userType,
					apptUser: userId,
					courseType: 1,
					courseId: courseIds[index],
					apptDate: nowTime
				}) 
				redisData.push({
					apptId: apptIds[index],
					apptUser: userId,
					courseType: 1,
					courseId: courseIds[index],
					apptStatus: 0,
					apptDate: nowTime
				})
			}
		}
		if(insertData.length != 0) {
			await this.redis.grab.set(`stask@${userId}`, stask.concat(redisData))
			this.mysql.cxxy.insert({
				table: 'grab_appt',
				field: [
					'apptId',
					'apptUserType',
					'apptUser',
					'courseType',
					'courseId',
					'apptDate'
				],
				data: insertData
			})
		}
		return courseIds
	}

	async createCourseGrabJob(majorId, startTime, endTime) {
		// const grabId = await this.common.md5('0' + majorId + startTime + endTime)
		// await this.mysql.cxxy.insert('grab_task', 'grabId, majorId, courseType, startTime, endTime', [grabId, majorId, 0, startTime, endTime])
		this.common.createJob(`grab@${majorId}`, new Date(startTime * 1000), {
			function: this._startGrabCourse.bind(this),
			params: [majorId, startTime, endTime]
		}, {
			function: () => {
				console.log('grab course task done')
			}
		}, (err) => {
			console.error(err)
		}, true)
	}

	async createSportsCourseGrabJob(majorId, startTime, endTime) {
		//const grabId = await this.common.md5('1' + majorId + startTime + endTime)
		// await this.mysql.cxxy.insert('grab_task', 'grabId, majorId, courseType, startTime, endTime', [grabId, majorId, 1, startTime, endTime])
		this.common.createJob(`grab@${majorId}`, new Date(startTime * 1000), {
			function: this.startGrabSportsCourse,
			params: [majorId, startTime, endTime]
		}, {
			function: () => {
				console.log('grab sports course task done')
			}
		}, (err) => {
			console.error(err)
		}, true)
	}

	async startGrabSportsCourse(majorId) {
		const grabTasks = await this.mysql.cxxy.select({
			table: [
				'grab_appt a',
				'grab_course c'
			],
			field: [
				'a.apptId',
				'a.apptUser',
				'a.courseType',
				'a.courseId',
				'c.courseTid',
				'c.courseData'
			],
			where: 'a.courseId=c.courseId && a.courseType=1 && c.courseType=1 && a.apptStatus=0 && c.courseCount>0 && majorId=$majorId',
			data: {
				majorId
			},
			sort: {
				field: 'apptDate',
				order: 'asc'
			}
		})
		for(let task of grabTasks) {
			const {apptId, apptUser, courseType, courseId, courseTid, courseData} = task

		}
	}

	async _startGrabCourse(majorId,  startTime, endTime) {
		this.common.createJob(`grabing@${majorId}`, '* * * * * *', {
			function: async () => {
				const nowTime = parseInt(Date.now() / 1000)
				if(nowTime < startTime) {
					console.log(`grab ${majorId} course not start`)
					this.common.stopJob(`grabing@${majorId}`)
					return
				}
				if(nowTime >= endTime) {
					console.log(`grab ${majorId} course is done`)
					this.common.stopJob(`grabing@${majorId}`)
					return
				}
				const keys = await this.redis.grab.find('ctask@*')
				if(keys.length == 0)
					return
				const courses = await this._getGrabCourseByMajorId(majorId)
				for(let key of keys) {
					const ctask = await this.redis.grab.get(key, true)
					if(!ctask || ctask.finish || !ctask.userId)
						continue
					let user = await this.redis.grab.get(`grabUser@${ctask.userId}`, true)
					if(!user || !user.userId || !user.data || !user.data.zf) {
						user = {
							userId: ctask.userId
						}
						const {stuId, stuPwd} = await this.hd.student.getStuDataByUserId(ctask.userId, 'stuId', 'stuPwd')
						await this.hd.zf.casLogin(stuId, stuPwd, user)
						await this.hd.zf.activeCasZfSession(0, user)
						await this.redis.grab.set(`grabUser@${ctask.userId}`, user)
					}
					console.log(`start grab ${ctask.userId} course`)
					let finishGrab = false
					let waitAppts = new Set()
					let finishAppts = new Set()
					let failedAppts = new Set()
					let failCount = 0
					while(finishGrab) {
						let targetData = {}
						let courseTids = {}
						for(let index in ctask.appts) {
							const {courseId, apptStatus} = ctask.appts[index]
							//等待抢课或者正在抢课
							if(apptStatus == 0 || apptStatus == 1) {
								//过滤出需要的数据
								for(let cIndex in courses) {
									const course = courses[cIndex]
									//如果已经进入等待抢课或正在抢课的状态就直接跳到下一步
									if(waitAppts.has(courseId))
										continue
									if(course.courseId == courseId) {
										const {courseType, courseTid, courseData, courseCount} = course
										let target = this.common.isJson(courseData)
										if(!target)
											return this.reject('-2031', 'grab course target data invalid')
										courseTids[courseTid] = courseId
										waitAppts.add(courseId)
										targetData[target.cb] = 'on'
										ctask.appts[index].apptStatus = 1
										break
									}
								}
								//数据准备完毕可以开抢
								if(index == ctask.appts.length - 1) {
									await this.redis.grab.set(key, ctask)
									const result = await this.hd.zf.sendGrabCourse(user, Object.keys(courseTids), targetData)
									//本次抢课成功
									if(result.length > 0) {
										for(let index in ctask.appts) {
											ctask.appts[index] = 2
										}
										for(let fTid of result) {
											if(waitAppts.has(courseTids))
												waitAppts.delete(courseTids[fTid])
											if(!finishAppts.has(courseId))
												finishAppts.add(courseTid[fTid])
										}
										//判断是否已经把全部需要抢的课抢到
										if(result.length == ctask.appts.length)
											ctask.finish = true
										await this.redis.grab.set(`ctask@${ctask.userId}`, ctask)
									}
									//本次抢课失败
									else {
										failCount++
										if(failCount > 30) {
											ctask.finish = true
											await this.redis.grab.set(`ctask@${ctask.userId}`, ctask)
										}
										console.log(`grab course failed retrying...`)
									}
								}
							}
							//抢课成功
							else if(apptStatus == 2) {
								//全部抢课成功
								if(finishAppts.size == ctask.appts.length)
									finishGrab = true
								//判断是否已加入成功集合过
								if(!finishAppts.has(courseId))
									finishAppts.add(courseId)
							}
							//抢课失败
							else if(appt.apptStatus == 3) {
								//全部抢课失败
								if(failedAppts.size == ctask.appts.length)
									finishGrab = true
								//判断是否已加入失败集合过
								if(!failedAppts.has(courseId))
									failedAppts.add(courseId)
							}
						}
					}
				}
			}
		}, {
			function: () => {
				console.log(`grab interval is done`)
			}
		}, (err) => {
			console.error(err)
		}, true, true)
	}

	

	async _addClassroom(roomName) {
		const roomId = this.crypto.md5(roomName)
		const result = await this.mysql.cxxy.select({
			table: 'classroom',
			field: 'id',
			where: 'roomId=$roomId',
			data: {
				roomId
			}
		})
		if(result.length == 0) {
			console.log(`add new classroom ${roomName}`)
			await this.mysql.cxxy.insert({
				table: 'classroom',
				field: [
					'roomId',
					'roomName'
				],
				data: {
					roomId,
					roomName
				}
			})
		}
		else
			console.log(`classroom  already exists ${roomName}`)
		return roomId
	}

	

}

module.exports = h_grab